package com.j.lemon.learn.thread.volatile原理;

/**
 * @Author lijunjun
 * @Date 2019-04-17 14:03
 * @Description volatile可见性验证
 *              volatile保证可见性，即每次线程读取数据时，直接从主内存中读取，不再从工作内存中读取
 */
public class Volatile extends Thread{
    private volatile boolean flag =true;

    private void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        while(flag){
        }
        System.out.println("flag==false,退出");
    }

    public static void main(String[] args) throws InterruptedException {
        Volatile v = new Volatile();
        v.start();
        Thread.sleep(1000);
        v.setFlag(false);




    }
}

/**
 * volatile禁止指令重排
 */
class Volatile1{
    int a =0;
    boolean flag = false;

    public void method1(){
        a=1;
        flag=true;
    }

    public void method2(){
        if(flag){
            a=a+5;
            System.out.println(a);
        }
    }
}


/**
 *
 * volatile使用场景
 *
 *
 * 单例模式，双重检查锁单例模式,如果不加volatile，不禁止指令重排，就会出问题，虽然几率很小。
 * singleton=new Singleton()，JVM执行顺序是：1.分配内存空间
 *                                         2.实例化对象singleton
 *                                         3.将singleton引用指向分配的内存空间,singleton就不为null了
 *
 * 如果不加volatile关键字，执行顺序可能是1-3-2（指令重排）,这就造成
 * 当1-3执行完，cpu被其他线程占据，此时虽然doubleCheckLock不为null（将引用指向了分配的内存空间）,
 * 但是并没有进行实例化，其他线程将得到一个不完整的对象.
 *
 *
 * 加了volatile之后，有两层含义：1.该变量是可见的，即每次读取都会到主内存中进行读取操作，每次读取到的都是最新的值。
 * 2.有序性，在进行指令优化时，不能将在对volatile变量访问的语句放在其后面执行，也不能把volatile变量后面的语句放到其前面执行。
 */
class Singleton{

    //如果不加volatile，
    private static volatile Singleton singleton;

    public Singleton getInstance(){
        //如果不加volatile，此处判断不为null，但是对象没有被实例化，只是分配了内存空间并把引用指向了内存空间，return的就是一个不完整的对象
        if(singleton==null){
            //两个线程同时走到这里
            synchronized (Singleton.class){
                //如果不加判断，两个线程都会new Singleton()
                if(singleton==null){
                    singleton=new Singleton();
                }
            }
        }
        return singleton;
    }

    private Singleton(){

    }
}
